專案開發到第四週,我很清楚地意識到一件事:
即使 Desktop 版做得再好,沒有手機版就是不完整。
因為大部分任務管理發生在移動中。在會議室、在咖啡廳、在通勤路上。
所以從一開始,我就把 Grimo 設計成跨平台架構。不是為了炫技,而是為了生存。
今天分享 KMP 跨平台擴展的實戰經驗。
Desktop 開發週期最短。不用處理 App Store 審核。除錯工具最完整。UI 迭代最快速。
一個人的資源有限,先做最容易成功的。
開發者主要在桌面工作。先滿足核心用戶。收集回饋後再擴展。降低試錯成本。
但架構要從第一天就考慮跨平台。
// shared module - 90% 的商業邏輯
expect class PlatformInfo {
val name: String
val version: String
}
// 核心業務邏輯完全共享
class ProjectRepository {
suspend fun getProject(id: String): Project {
// 資料庫操作、網路請求、商業邏輯
// 在所有平台上都一樣
}
}
// UI 狀態管理也共享
class ProjectViewModel {
val uiState: StateFlow<ProjectUiState>
// MVI 模式在所有平台通用
}
// desktopMain
actual class PlatformInfo {
actual val name = "Desktop"
actual val version = System.getProperty("os.version")
}
// androidMain
actual class PlatformInfo {
actual val name = "Android"
actual val version = Build.VERSION.RELEASE
}
// iosMain
actual class PlatformInfo {
actual val name = "iOS"
actual val version = UIDevice.currentDevice.systemVersion
}
Local-First 是核心
每個裝置都有完整的本地資料庫。離線優先,線上同步。用戶永遠不會因為網路問題無法工作。
// 通用的同步介面
interface SyncStrategy {
suspend fun sync(): Result<SyncStatus>
}
// Desktop:檔案系統同步
class DesktopSyncStrategy : SyncStrategy {
override suspend fun sync() =
fileSystemSync() // 透過 Dropbox/iCloud
}
// Mobile:API 同步
class MobileSyncStrategy : SyncStrategy {
override suspend fun sync() =
apiSync() // 透過後端 API
}
Compose Multiplatform vs Native UI
這是最難的決定。
Compose Multiplatform 的優點:
但我選擇了 Native UI:
為什麼?
因為用戶體驗。
iOS 用戶期待 iOS 的操作感。Android 用戶期待 Material Design。強行統一反而造成困擾。
// shared module
class ProjectViewModel {
private val _state = MutableStateFlow(ProjectState())
val state: StateFlow<ProjectState> = _state
fun loadProject(id: String) {
viewModelScope.launch {
repository.getProject(id)
.onSuccess { _state.update { it.copy(project = project) } }
.onFailure { _state.update { it.copy(error = error) } }
}
}
}
iOS 端使用:
// iOS SwiftUI
struct ProjectView: View {
@StateObject private var viewModel = ProjectViewModel()
var body: some View {
// 直接使用 KMP 的 ViewModel
if let project = viewModel.state.project {
Text(project.name)
}
}
}
Android 端使用:
// Android Compose
@Composable
fun ProjectScreen(viewModel: ProjectViewModel) {
val state by viewModel.state.collectAsState()
state.project?.let { project ->
Text(project.name)
}
}
神奇的是,商業邏輯完全一樣!
設定 iOS target。建立 SwiftUI 專案。連接 KMP framework。測試基本功能。
最難的部分?Xcode 和 Gradle 的整合。
解決方案:
// build.gradle.kts
kotlin {
ios {
binaries {
framework {
baseName = "GrimoShared"
// 關鍵:export 需要的類別
export(project(":shared"))
}
}
}
}
SwiftUI 其實不難學。特別是有 Compose 經驗後。
概念都是響應式 UI:
// SwiftUI - 很像 Compose
struct TaskListView: View {
@State private var tasks: [Task] = []
var body: some View {
List(tasks) { task in
TaskRow(task: task)
}
}
}
重點是保持 Native 體驗。不要強行移植 Desktop 的操作邏輯。
這週最累。
處理 iOS 特定功能:推送通知、Face ID、深色模式、手勢操作。
但因為核心邏輯都在 KMP,真正要寫的程式碼很少。
有了 iOS 經驗,Android 版只花了一週。
為什麼這麼快?
// 90% 的程式碼直接複用
@Composable
fun ProjectScreenAndroid() {
// 幾乎和 Desktop 版一樣
ProjectScreen(
modifier = Modifier.fillMaxSize(),
// 只需要調整一些平台特定行為
onBackPressed = { activity.onBackPressed() }
)
}
// Widget 支援
struct GrimoWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "TaskWidget") { entry in
TaskWidgetView(entry: entry)
}
}
}
// Shortcuts
struct GrimoShortcuts {
static func setupShortcuts() {
let shortcut = UIApplicationShortcutItem(
type: "new_task",
localizedTitle: "新增任務"
)
}
}
// Material You 動態主題
@Composable
fun GrimoTheme(
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= 31 -> {
dynamicDarkColorScheme(LocalContext.current)
}
else -> DefaultColorScheme
}
}
// 快速設定磚
class QuickTaskTileService : TileService() {
override fun onClick() {
// 快速新增任務
}
}
KMP 讓跨平台開發變得可行。對一人公司來說,這是遊戲規則改變者。
寫一次商業邏輯,三個平台都能用。維護成本大幅降低。更新功能時,只需要改一個地方。
如果你要做跨平台應用:
資源有限:選 Flutter 或 React Native,快速出貨。
長期維護:選 KMP,程式碼品質高,維護成本低。
用戶體驗優先:Native 開發,但成本最高。
我的建議:
選擇最能快速驗證的平台。
無論選什麼技術,記住:
分離關注點 - UI 和業務邏輯要分開。統一資料流 - 所有平台用同樣的狀態管理。漸進式擴展 - 不要一開始就做所有平台。
從 Desktop 到 iOS 再到 Android,Grimo 的跨平台之旅證明了:
一人公司也能開發多平台應用。
關鍵不是技術有多強,而是架構設計是否合理。
KMP 給了我們一個選擇:用較少的資源,達到較好的結果。
當你的 Desktop 應用運行穩定,當用戶開始詢問「有手機版嗎?」,當你看到 KMP 編譯出 iOS framework...
你會發現,跨平台不再是夢想,而是週末就能實現的計劃。
記住:平台是手段,解決問題才是目的。